﻿using System;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Runtime.Serialization.Formatters.Binary;
using Chilkat;

namespace ComicReader
{
    public class Uncompress : IDisposable
    {
        private Rar rar = new Rar();
        private Zip zip = new Zip();
        public static string temp = Environment.GetEnvironmentVariable("TEMP") + "\\ComicReader\\";
        private string tempPath;
        private string filename;

        public string TempPath
        {
            get { return this.tempPath; }
        }

        public Uncompress(string path)
        {
            this.filename = path;
            this.tempPath = temp + Uncompress.createMD5(this.filename.Substring(this.filename.LastIndexOf('\\')));
            Uncompress.CleanDirectory(this.tempPath);
            Directory.CreateDirectory(this.tempPath);

            if (!File.Exists(this.filename))
                throw new FileNotFoundException("File " + this.filename + " not found.");

            if (rar.Open(this.filename))
            {
                if (rar.Unrar(this.tempPath))
                    return;
                else
                    throw new Exception(rar.LastErrorText);
            }

            if (!zip.UnlockComponent(Uncompress.RandomString(16,true)))
                throw new Exception("Some happen when unlocking... " + zip.LastErrorText);

            if (zip.OpenZip(this.filename))
            {
                if (zip.Unzip(this.tempPath) != -1)
                    return;
                else
                    throw new Exception(zip.LastErrorText);
            }

            Uncompress.CleanDirectory(this.tempPath);
            throw new FileLoadException("Error: Can't open file " + this.filename + ".");
        }

        ~Uncompress()
        {
            Dispose(false);
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            this.rar.Dispose();
            this.zip.Dispose();
        }

        public static void CleanDirectory(string path)
        {
            try
            {
                if (path == null)
                    Directory.Delete(temp, true);
                if (Directory.Exists(path))
                    Directory.Delete(path, true);
            }
            catch (Exception e)
            {
                throw new Exception(e.Message);
            }
        }

        /// <summary>
        /// Create a string's MD5 hash code
        /// </summary>
        /// <param name="str">String to create a hash code</param>
        /// <returns>MD5 hash code</returns>
        public static string createMD5(string str)
        {
            try
            {
                MD5CryptoServiceProvider x = new MD5CryptoServiceProvider();
                byte[] bs = Encoding.UTF8.GetBytes(str);
                bs = x.ComputeHash(bs);
                StringBuilder s = new StringBuilder();
                foreach (byte b in bs)
                {
                    s.Append(b.ToString("x2").ToLower());
                }
                return s.ToString();
            }
            catch (Exception e)
            {
                throw new CryptographicException("Can't create md5 hash of string: \"" + str + "\" with error: " + e.Message);
            }
        }

        /// <summary>
        /// Generates a random string with the given length
        /// </summary>
        /// <param name="size">Size of the string</param>
        /// <param name="lowerCase">If true, generate lowercase string</param>
        /// <returns>Random string</returns>
        public static string RandomString(int size, bool lowerCase)
        {
            StringBuilder builder = new StringBuilder();
            Random random = new Random();
            char ch;
            for (int i = 0; i < size; i++)
            {
                ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
                builder.Append(ch);
            }
            if (lowerCase)
                return builder.ToString().ToLower();
            return builder.ToString();
        }
    }
}
